/*

* ------------------------------------------------------------------------
* ------------------------------------------------------------------------
* |     Smart-Shop开源商城系统/ Java商城-首选电商平台系统 请务必保留此注释头信息
* |     开源地址: https://gitee.com/ningbo-qishan/gruul-mall
* ------------------------------------------------------------------------
* ------------------------------------------------------------------------
* |     可正常分享和学习源码,未经授权 不可商用！
* |     商业版中台购买/开源版商业授权 联系技术客服
* |     官网:  https://www.73app.cn/
* ------------------------------------------------------------------------
* ------------------------------------------------------------------------
* |     Copyright (c) 2016-2999 宁波启山智软科技有限公司
* |     版权所有 ,侵权必究！
* ------------------------------------------------------------------------

*/
package com.medusa.gruul.live.utils;

import cn.hutool.crypto.SecureUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.alibaba.fastjson.parser.Feature;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.medusa.gruul.common.core.constant.RegexConstants;
import com.medusa.gruul.common.core.exception.ServiceException;
import com.medusa.gruul.common.core.util.SystemCode;
import com.medusa.gruul.payment.api.util.GlobalConstant;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.ParseException;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.client.utils.HttpClientUtils;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.ReflectionUtils;

import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.io.*;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.Inet4Address;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Pattern;

import org.apache.commons.codec.binary.Hex;
import org.bouncycastle.openssl.PasswordFinder;
import org.bouncycastle.openssl.PEMReader;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;


/**
 * <p>
 *  常用工具封装
 *  isBlank:    str空效验
 *  isNotBlank: str非空效验
 *  copySameNotNullFields: obj非空字段copy from有get to有set
 *  getMap: obj To Map
 * </p>
 *
 * @author lcysike
 * @since 2021-01-12
 */
public class GlobalTools {

    private static Logger log = LoggerFactory.getLogger("debug");

    private GlobalTools() {

    }


    public static void threadSleep(Long ms) {
        try {
            Thread.sleep(ms);
        } catch (Exception e) {
        }
    }


    public static boolean isBlank(String str) {
        int length;
        if (null == str || ((length = str.length())) == 0) {
            return Boolean.TRUE;
        }

        for (int i = 0; i < length; i++) {
            if (!Character.isWhitespace(str.charAt(i))) {
                return Boolean.FALSE;
            }
        }

        return Boolean.TRUE;
    }


    public static boolean isNotBlank(Map map) {
        return !isBlank(map);
    }


    public static boolean isNotBlank(Long value) {
        return !isBlank(value);
    }

    public static boolean isBlank(List list) {
        return null == list || list.isEmpty();
    }

    public static boolean isNotBlank(List list) {
        return !isBlank(list);
    }

    public static boolean isBlank(Long value) {
        return null == value;
    }

    public static boolean isNotBlank(Set<Object> set) {
        return !isBlank(set);
    }

    public static boolean isBlank(Set set) {
        return null == set || set.isEmpty();
    }


    public static boolean isBlank(Map map) {
        return null == map || map.size() <= 0;
    }


    public static boolean isNotBlank(String str) {
        return !StringUtils.isBlank(str);
    }


    public static <T> T copySameNotNullFields(Object fromObj, T toObj) {
        if (null == fromObj) {
            return null;
        }

        if (null == toObj) {
            return JSON.parseObject(JSON.toJSONString(fromObj), new TypeReference<T>() {
            }.getType(), Feature.IgnoreNotMatch);
        }

        Class<?> fromObjClass = fromObj.getClass();

        ReflectionUtils.doWithMethods(toObj.getClass(), (Method m) -> {
            String name = "g" + m.getName().substring(1);
            Method methodGet = ReflectionUtils.findMethod(fromObjClass, name);
            if (null != methodGet) {
                try {
                    Object value = ReflectionUtils.invokeMethod(methodGet, fromObj);
                    if (null != value) {
                        if (value instanceof String) {
                            if (isNotBlank((String) value)) {
                                ReflectionUtils.invokeMethod(m, toObj, value);
                            }
                        } else {
                            ReflectionUtils.invokeMethod(m, toObj, value);
                        }
                    }
                } catch (Exception e) {
                }
            }
        }, method -> method.getName().startsWith("set"));
        return toObj;
    }


    public static <T> T mapToBean(Map<String, Object> map, Class<?> clazz) {
        Object obj;
        try {
            obj = clazz.newInstance();
        } catch (Exception e) {
            return null;
        }

        if (isNotBlank(map)) {
            for (Map.Entry<String, Object> entry : map.entrySet()) {
                String propertyName = entry.getKey();
                Object value = entry.getValue();
                String setMethodName = GlobalConstant.STRING_SET + propertyName.substring(0, 1).toUpperCase() + propertyName.substring(1);
                Field field = getClassField(clazz, propertyName);
                if (null == field) {
                    continue;
                }
                Class<?> fieldTypeClass = field.getType();
                value = convertValType(value, fieldTypeClass);
                if (null != value && value instanceof String) {
                    value = value.toString();
                }

                if (value instanceof JSONObject) {

                }
                Method method = ReflectionUtils.findMethod(clazz, setMethodName, field.getType());
                ReflectionUtils.invokeMethod(method, obj, value);
            }
        }
        return (T) obj;
    }


    private static Field getClassField(Class<?> clazz, String fieldName) {
        if (Object.class.getName().equals(clazz.getName())) {
            return null;
        }
        Field[] declaredFields = clazz.getDeclaredFields();
        for (Field field : declaredFields) {
            if (field.getName().equals(fieldName)) {
                return field;
            }
        }
        Class<?> superClass = clazz.getSuperclass();
        if (superClass != null) {
            return getClassField(superClass, fieldName);
        }
        return null;
    }


    private static Object convertValType(Object value, Class<?> fieldTypeClass) {
        Object retVal = null;
        if (Long.class.getName().equals(fieldTypeClass.getName())
                || long.class.getName().equals(fieldTypeClass.getName())) {
            retVal = Long.parseLong(value.toString());
        } else if (Integer.class.getName().equals(fieldTypeClass.getName())
                || int.class.getName().equals(fieldTypeClass.getName())) {
            retVal = Integer.parseInt(value.toString());
        } else if (Float.class.getName().equals(fieldTypeClass.getName())
                || float.class.getName().equals(fieldTypeClass.getName())) {
            retVal = Float.parseFloat(value.toString());
        } else if (Double.class.getName().equals(fieldTypeClass.getName())
                || double.class.getName().equals(fieldTypeClass.getName())) {
            retVal = Double.parseDouble(value.toString());
        } else {
            retVal = value;
        }
        return retVal;
    }


    public static Map<String, String> getMap(Object obj) {
        if (null == obj) {
            return newHashMap(0);
        }

        Map<String, String> map = newHashMap(32);

        ReflectionUtils.doWithMethods(obj.getClass(), (Method m) -> {
            String name = m.getName().substring(3);
            name = name.substring(0, 1).toLowerCase() + name.substring(1);
            Object value = ReflectionUtils.invokeMethod(m, obj.getClass());
            if (null != value) {
                map.put(name, value.toString());
            }
        }, method -> (method.getName().startsWith("get") && !"getClass".equals(method.getName())));

        return map;
    }


    public static Map newHashMap(int i) {
        return new HashMap<>(i);
    }


    public static HashMap newHashMap() {
        return Maps.newHashMap();
    }


    public static Map newLinkedHashmap(int i) {
        return new LinkedHashMap(i);
    }


    public static LinkedHashMap newLinkedHashmap() {
        return Maps.newLinkedHashMap();
    }


    public static List newArrayList(int i) {
        return new ArrayList(i);
    }


    public static ArrayList newArrayList() {
        return Lists.newArrayList();
    }


    public static LinkedList newLinkedList() {
        return Lists.newLinkedList();
    }


    public static void checkPhoneNumber(String iphone) {
        int maxLength = 11;
        String phoneNumber = iphone;
        if (phoneNumber.length() != maxLength) {
            throw new ServiceException(String.format("send fail. phone number length too long!  %s", SystemCode.PARAM_VALID_ERROR.getMsg()));
        }

        if (!Pattern.compile(RegexConstants.REGEX_MOBILE_EXACT).matcher(phoneNumber).matches()) {
            throw new ServiceException(String.format("send fail. phone number is invalid!  %s", SystemCode.PARAM_VALID_ERROR.getMsg()));
        }
    }


    public static ConcurrentHashMap newConcurrentHashMap(int i) {
        return new ConcurrentHashMap(i);
    }


    public static String getLocalIp() {
        return Inet4Address.getLoopbackAddress().getHostAddress();
    }


    /**
     * AES证书加密
     */
    public static String encryptAes(String key, String data) {
        final int a = 16;
        final int b = 24;
        final int c = 32;
        final int one = 1;
        try {
            String charset = GlobalConstant.STRING_UTF_8;
            if (key == null) {
                throw new RuntimeException("key is null");
            } else if (a != key.length() && b != key.length() && c != key.length()) {
                throw new RuntimeException("key must be 16/24/32");
            } else {
                byte[] raw = key.getBytes(charset);
                SecretKeySpec sKeySpec = new SecretKeySpec(raw, GlobalConstant.STRING_AES);
                Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
                cipher.init(one, sKeySpec);
                byte[] encrypted = cipher.doFinal(data.getBytes(charset));
                return parseByte2HexStr(encrypted);
            }
        } catch (Exception var3) {
            throw new RuntimeException(var3);
        }
    }


    /**
     * AES证书解密
     */
    public static String decryptAes(String key, String text) {
        final int a = 16;
        final int b = 24;
        final int c = 32;
        final int two = 2;
        try {
            if (key == null) {
                throw new RuntimeException("key is null");
            } else if (a != key.length() && b != key.length() && c != key.length()) {
                throw new RuntimeException("key must be 16/24/32");
            } else {
                byte[] raw = key.getBytes(GlobalConstant.STRING_UTF_8);
                SecretKeySpec sKeySpec = new SecretKeySpec(raw, "AES");
                Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
                cipher.init(two, sKeySpec);

                try {
                    byte[] original = cipher.doFinal(parseHexStr2Byte(text));
                    return new String(original, GlobalConstant.STRING_UTF_8);
                } catch (Exception var9) {
                    throw new RuntimeException(var9);
                }
            }
        } catch (Exception var3) {
            throw new RuntimeException(var3);
        }
    }


    /**
     * 根据rsa私钥进行签名
     */
    public static String signSha256withRsa(String privateKey, String privateKeyPwd, String data) {
        return signSha256withRsa(privateKey, privateKeyPwd, data, GlobalConstant.STRING_UTF_8);
    }


    private static String signSha256withRsa(String privateKey, String privateKeyPwd, String data, String charset) {
        try {
            PrivateKey privateKeyObj = loadPrivateKey(privateKey, privateKeyPwd);
            return Hex.encodeHexString(signSha256(privateKeyObj, getBytes(data, charset)));
        } catch (Exception var4) {
            throw new RuntimeException(var4);
        }
    }


    /**
     * 获取参数拼接
     */
    public static String getParamStr(Map<String, Object> paramMap) {
        if (paramMap == null) {
            return null;
        }
        SortedMap<String, Object> map = new TreeMap<>(paramMap);
        StringBuilder stringBuffer = new StringBuilder();
        for (Map.Entry<String, Object> m : map.entrySet()) {
            Object value = m.getValue();
            if (value != null && !isBlank(String.valueOf(value))) {
                stringBuffer.append(m.getKey()).append("=").append(value).append("&");
            }
        }
        stringBuffer.delete(stringBuffer.length() - 1, stringBuffer.length());
        return stringBuffer.toString();
    }


    private static PrivateKey loadPrivateKey(String privateKeyStr, final String pwd) {
        try {
            Security.addProvider(new BouncyCastleProvider());
            PEMReader reader = new PEMReader(new StringReader(privateKeyStr), new PasswordFinder() {
                @Override
                public char[] getPassword() {
                    return pwd.toCharArray();
                }
            });
            KeyPair pair = (KeyPair) reader.readObject();
            Key privateKey = pair.getPrivate();
            reader.close();
            return (PrivateKey) privateKey;
        } catch (Exception var5) {
            throw new RuntimeException("load privateKey error:", var5);
        }
    }


    private static byte[] signSha256(PrivateKey privateKey, byte[] data) {
        try {
            Signature signature = Signature.getInstance("SHA256withRSA");
            signature.initSign(privateKey);
            signature.update(data);
            return signature.sign();
        } catch (Exception var5) {
            throw new RuntimeException(var5);
        }
    }


    public static byte[] sxfSign(String content, String privateKey) {
        try {
            PKCS8EncodedKeySpec priPkcs8 = new PKCS8EncodedKeySpec(decryptBase64(privateKey));
            PrivateKey priKey = KeyFactory.getInstance(GlobalConstant.STRING_RSA).generatePrivate(priPkcs8);
            java.security.Signature signature = java.security.Signature.getInstance(GlobalConstant.STRING_SHA1_WITH_RSA);
            signature.initSign(priKey);
            signature.update(content.getBytes());
            byte[] signed = signature.sign();
            return signed;
        } catch (Exception e) {
            log.warn(e.getMessage());
            return null;
        }
    }


    /**
     * 根据rsa公钥进行验签
     */
    public static boolean verifySha256withRsa(String publicKey, String data, String sign) {
        return verifySha256withRsa(publicKey, data, sign, GlobalConstant.STRING_UTF_8);
    }


    private static boolean verifySha256withRsa(String publicKey, String data, String sign, String charset) {
        try {
            PublicKey publicKeyObj;
            try {
                Security.addProvider(new BouncyCastleProvider());
                PEMReader reader = new PEMReader(new StringReader(publicKey));
                publicKeyObj = (PublicKey) reader.readObject();
            } catch (Exception var2) {
                throw new RuntimeException("load publicKey error:", var2);
            }
            try {
                Signature signature = Signature.getInstance("SHA256withRSA");
                signature.initVerify(publicKeyObj);
                signature.update(getBytes(data, charset));
                return signature.verify(Hex.decodeHex(sign.toCharArray()));
            } catch (Exception var5) {
                throw new RuntimeException(var5);
            }
        } catch (Exception var5) {
            throw new RuntimeException(var5);
        }
    }

    private static String parseByte2HexStr(byte[] buf) {
        StringBuilder sb = new StringBuilder();

        for (byte aBuf : buf) {
            String hex = Integer.toHexString(aBuf & 255);
            if (hex.length() == 1) {
                hex = GlobalConstant.STRING_ZERO + hex;
            }
            sb.append(hex.toLowerCase());
        }
        return sb.toString();
    }

    private static byte[] parseHexStr2Byte(String hexStr) {
        if (hexStr.length() < 1) {
            return null;
        } else {
            byte[] result = new byte[hexStr.length() / 2];

            for (int i = 0; i < hexStr.length() / 2; ++i) {
                int high = Integer.parseInt(hexStr.substring(i * 2, i * 2 + 1), 16);
                int low = Integer.parseInt(hexStr.substring(i * 2 + 1, i * 2 + 2), 16);
                result[i] = (byte) (high * 16 + low);
            }
            return result;
        }
    }

    private static byte[] getBytes(String content, String charset) {
        if (content == null) {
            content = GlobalConstant.STRING_EMPTY;
        }
        if (isBlank(charset)) {
            throw new IllegalArgumentException("charset can not null");
        } else {
            try {
                return content.getBytes(charset);
            } catch (UnsupportedEncodingException var3) {
                throw new RuntimeException(String.format("charset is not valid,charset is: %s", charset));
            }
        }
    }


    public static Map<String, Object> getObjectMap(Object object) {
        Map<String, Object> map = newHashMap();
        try {
            if (object == null) {
                return null;
            }
            BeanInfo beanInfo;
            beanInfo = Introspector.getBeanInfo(object.getClass());
            PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
            for (PropertyDescriptor property : propertyDescriptors) {
                String key = property.getName();
                if (key.compareToIgnoreCase("class") == 0) {
                    continue;
                }
                Method getter = property.getReadMethod();
                Object value = getter != null ? getter.invoke(object) : null;
                map.put(key, value);
            }
        } catch (Exception e) {
            log.warn(e.getMessage());
        }
        return map;
    }


    public static String post(String url, Map<String, String> params) {
        CloseableHttpClient httpClient = HttpClients.createDefault();
        String body = null;

        log.info("create httppost:" + url);
        HttpPost post = postForm(url, params);

        body = invoke(httpClient, post);

        HttpClientUtils.closeQuietly(httpClient);

        return body;
    }


    private static HttpPost postForm(String url, Map<String, String> params) {

        HttpPost httpost = new HttpPost(url);
        List<NameValuePair> nvps = new ArrayList<NameValuePair>();

        Set<String> keySet = params.keySet();
        for (String key : keySet) {
            nvps.add(new BasicNameValuePair(key, params.get(key)));
        }

        try {
            log.warn("set utf-8 form entity to httppost");
            httpost.setEntity(new UrlEncodedFormEntity(nvps, GlobalConstant.STRING_UTF_8));
        } catch (UnsupportedEncodingException e) {
            log.warn(e.getMessage());
        }

        return httpost;
    }


    public static String post(String url, String params) {
        CloseableHttpClient httpClient = HttpClients.createDefault();
        String body = null;

        log.info("create httppost:" + url);
        HttpPost post = postForm(url, params);

        body = invoke(httpClient, post);

        HttpClientUtils.closeQuietly(httpClient);

        return body;
    }


    private static HttpPost postForm(String url, String params) {
        HttpPost httpost = new HttpPost(url);
        StringEntity entity = new StringEntity(params, GlobalConstant.STRING_UTF_8);
        entity.setContentType("application/json");
        try {
            log.info("set utf-8 form entity to http post");
            httpost.setEntity(entity);
        } catch (Exception e) {
            log.warn(e.getMessage());
        }
        return httpost;
    }


    private static String invoke(CloseableHttpClient httpclient, HttpUriRequest httpost) {
        HttpResponse response = sendRequest(httpclient, httpost);
        return parseResponse(response);
    }


    private static HttpResponse sendRequest(CloseableHttpClient httpclient,
                                            HttpUriRequest httpost) {
        log.info("execute post...");
        HttpResponse response = null;

        try {
            response = httpclient.execute(httpost);
        } catch (ClientProtocolException e) {
            log.warn(e.getMessage());
        } catch (IOException e) {
            log.warn(e.getMessage());
        }
        return response;
    }


    private static String parseResponse(HttpResponse response) {
        log.info("get response from http server..");
        HttpEntity entity = response.getEntity();

        log.info("response status: " + response.getStatusLine());

        String body = null;
        try {
            body = EntityUtils.toString(entity);
            log.info(body);
        } catch (ParseException e) {
            log.warn(e.getMessage());
        } catch (IOException e) {
            log.warn(e.getMessage());
        }
        return body;
    }


    public static String encryptBase64(byte[] key) {
        return (new BASE64Encoder()).encode(key);
    }


    public static byte[] decryptBase64(String key) throws Exception {
        return (new BASE64Decoder()).decodeBuffer(key);
    }


    /**
     * SXF RSA验签名检查
     *
     * @param content   待签名数据
     * @param sign      签名值
     * @param publicKey 分配给开发商公钥
     * @return 布尔值
     */
    public static boolean doSxfCheck(String content, String sign, String publicKey) {
        try {
            KeyFactory keyFactory = KeyFactory.getInstance(GlobalConstant.STRING_RSA);
            byte[] encodedKey = decryptBase64(publicKey);
            PublicKey pubKey = keyFactory.generatePublic(new X509EncodedKeySpec(encodedKey));
            java.security.Signature signature = java.security.Signature.getInstance(GlobalConstant.STRING_SHA1_WITH_RSA);
            signature.initVerify(pubKey);
            signature.update(content.getBytes());
            return signature.verify(decryptBase64(sign));
        } catch (Exception e) {
            log.warn(e.getMessage());
            return false;
        }
    }


    /**
     * sft 校验
     *
     * @param content
     * @param sign
     * @param aliPublicKey
     * @param inputCharset
     * @return
     */
    public static boolean verify(String content, String sign, String aliPublicKey, String inputCharset) {
        try {
            KeyFactory keyFactory = KeyFactory.getInstance(GlobalConstant.STRING_RSA);
            byte[] encodedKey = SecureUtil.decode(aliPublicKey);
            PublicKey pubKey = keyFactory.generatePublic(new X509EncodedKeySpec(encodedKey));
            java.security.Signature signature = java.security.Signature
                    .getInstance(GlobalConstant.STRING_SHA1_WITH_RSA);
            signature.initVerify(pubKey);
            signature.update(content.getBytes(inputCharset));
            return signature.verify(SecureUtil.decode(sign));
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

}
